import moduleImg from '@/assets/image/screenshot/module.png';
import dynamicInjectImg from '@/assets/image/screenshot/dynamic-inject.png';
import pnpmInstallImg from '@/assets/image/screenshot/pnpm-install.png';

### PageSpy Module Composition#module

Dependency and interaction diagram between PageSpy modules:

<a href={moduleImg} target="_blank">
  <img alt="System module diagram" src={moduleImg} />
</a>

### Compatibility of PageSpy#compatibility

- The compatibility target for browser SDK is set to [`["chrome > 75","safari > 12", "> 0.1%", "not dead","not op_mini all"]`](https://github.com/HuolalaTech/page-spy/blob/main/packages/page-spy-browser/package.json#L66-L72). For other SDKs, please refer to their respective repositories.
- The debugging client is primarily for developers, and we maintain an open attitude towards new browser features. It is recommended to use the latest browser version. The compatibility target is set to [`["last 2 chrome version", "last 2 firefox version", "last 2 safari version"]`](https://github.com/HuolalaTech/page-spy-web/blob/main/package.json#L92-L96).

### How to Hide the SDK Rendered Logo?#hide-logo

```js
window.$pageSpy = new PageSpy({
  // ... other configuration parameters
  autoRender: false,
});
```

### What parameters can be passed during instantiation, and what do they do?#init-params

See [PageSpy API](./pagespy#constructor).

### How to Update Initialization Parameters? #update-info

PageSpy provides a "Device ID" for identifying devices and also offers `project / title` for developers to customize information during initialization to aid in client identification. If you wish to update these parameters after initialization, follow these steps:

```js
window.$pageSpy = new PageSpy(...);

// Calling updateRoomInfo updates project / title
window.$pageSpy.updateRoomInfo({ project: 'xxx', title: 'xxx' });
```

### How to Integrate with xxx Framework?#framework

PageSpy has published integration guides for mainstream frameworks on the CodeSandbox platform. You can experience them online:

- **React**：[CodeSandbox - PageSpy in React](https://codesandbox.io/p/sandbox/page-spy-with-react-k3pzzt)
- **Vue**：[CodeSandbox - PageSpy in Vue](https://codesandbox.io/p/sandbox/page-spy-with-vue-ft35qs)
- **Svelte**：[CodeSandbox - PageSpy in Svelte](https://codesandbox.io/p/sandbox/page-spy-with-svelte-p6mxd6)
- **Angular**：[CodeSandbox - PageSpy in Angular](https://codesandbox.io/p/sandbox/page-spy-with-angular-6wg3ps)
- **Nextjs**：[CodeSandbox - PageSpy in Nextjs](https://codesandbox.io/p/sandbox/page-spy-with-nextjs-5htxv5)
- **Nuxtjs**：[CodeSandbox - PageSpy in Nuxtjs](https://codesandbox.io/p/sandbox/page-spy-with-nuxtjs-8znq22)

### Is pagespy.jikejishu.com an Officially Provided Domain?#test-domain

[https://pagespy.jikejishu.com](https://pagespy.jikejishu.com) is a temporary service we set up to allow everyone to experience and learn PageSpy online. **We do not guarantee 24/7 availability, data security, or assume responsibility for any losses**. We strongly recommend deploying to your private server or intranet after experiencing it.

### Local port 6752 can be accessed, but not when deployed on a server?#server-port

Check whether the firewall or security group rules on the server have opened port 6752.

### What does "No client in the current connection" mean on the debug button?#debug-disabled

This usually occurs because the SDK successfully created a room but failed to join it via WebSocket. Follow these troubleshooting steps:

- Open the console on the client where the SDK is running and check for errors
- If the console shows "WebSocket connect failed" related messages, verify that the server configuration is correct

### How Should nginx Be Configured During Deployment? #nginx

Here's an nginx configuration for https://pagespy.jikejishu.com:

```nginx
server {
  listen 443 ssl;
  server_name pagespy.jikejishu.com;

  if ($scheme != https) {
      rewrite ^(.*)$  https://$host$1 permanent;
  }

  ssl_certificate /etc/letsencrypt/live/pagespy.jikejishu.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/pagespy.jikejishu.com/privkey.pem;

  location / {
      proxy_pass http://127.0.0.1:6752;
      proxy_http_version    1.1;
      proxy_set_header      Upgrade $http_upgrade;
      proxy_set_header      Connection "upgrade";
  }
}

server {
  if ($host = pagespy.jikejishu.com) {
      return 301 https://$host$request_uri;
  }

  listen 80;
  listen [::]:80;
  server_name pagespy.jikejishu.com;
  return 404;
}
```

### How to Deploy to a Subpath? #sub-path

Starting from version 1.5.4, you can deploy the service to a subpath. The installation process remains unchanged, but nginx configuration needs adjustment:

```nginx
server {
  # ...

  # <sub-path> should be replaced with the subpath you intend to deploy
  location /<sub-path>/  {
      # Ensure <sub-path> is consistent with above
      rewrite ^/<sub-path>/(.*)$ /$1 break;
      proxy_pass            http://127.0.0.1:6752;
      proxy_http_version    1.1;
      proxy_set_header      Upgrade $http_upgrade;
      proxy_set_header      Connection "upgrade";
      proxy_set_header      Host $host;
      proxy_set_header      X-Real-IP $remote_addr;
      proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
  }

  # Ensure <sub-path> is consistent with above
  location /<sub-path> {
      return 301 $scheme://$host$request_uri/;
  }
}
```

After adjusting the configuration, restart Nginx to access via the subpath. Note that during instantiation, you must manually pass `api` and `clientOrigin` to inform the SDK of the deployment address:

```js
window.$pageSpy = new PageSpy({
  // For example api: "example.com/pagespy"
  api: '<host>/<sub-path>',

  // For example clientOrigin: "https://example.com/pagespy"
  clientOrigin: '<scheme>://<host>/<sub-path>',
});
```

### How can the debug client be protected with authentication so only authorized developers can access?#security

Starting from version [2.3.0](./changelog#v2-3-0), PageSpy supports setting a password to protect the debugging panel. After setting a password, developers must enter the correct password to access the debugging panel.

Configurable variables:

- `AUTH_PASSWORD`: Set password
- `JWT_SECRET`: Set Token secret key
- `JWT_EXPIRATION_HOURS`: Set Token expiration time (hours), default is 24 hours

Set during service startup, the usage is as follows:

:::code-group

```bash docker
docker run -d --restart=always -v ./log:/app/log -v ./data:/app/data -p 6752:6752 --name="pageSpy" -e AUTH_PASSWORD=<password> -e JWT_SECRET=<secret> -e JWT_EXPIRATION_HOURS=<hours> ghcr.io/huolalatech/page-spy-web:latest
```

```bash node
AUTH_PASSWORD=<password> JWT_SECRET=<secret> JWT_EXPIRATION_HOURS=<hours> page-spy-api
```

:::

### Don't want to integrate manually into the project, can it be done without invading the business code?#extension

PageSpy provides a browser extension with the following features:

- Automatically inject the latest version of the SDK
- Automatically complete the instantiation operation
- Provide domain injection configuration rules

Click here to use: [HuolalaTech/page-spy-extension](https://github.com/HuolalaTech/page-spy-extension)

### Can I use a Tampermonkey script?#tampermonkey

Refer to the following content:

```js
// ==UserScript==
// @name         Inject PageSpy Script
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Inject script on xxx.yyy
// @author       You
// @match        <match rules, such as example.com>
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  var script1 = document.createElement('script');
  script1.setAttribute('crossorigin', 'anonymous');
  // Replace the SDK URL link with the actual project URL
  script1.src = 'https://pagespy.jikejishu.com/page-spy/index.min.js';

  var script2 = document.createElement('script');
  script2.textContent = 'window.$pageSpy = new PageSpy();';

  document.head.prepend(script1);
  script1.onload = () => {
    document.head.appendChild(script2);
  };
})();
```

### Business project is deployed on HTTPS, PageSpy is deployed on HTTP, how to resolve console errors?#http-error

Browsers block loading HTTP resources from HTTPS sites because HTTPS provides encryption and security, while HTTP transmits data in plaintext, posing security risks.

It is recommended to upgrade PageSpy to an HTTPS service to perfectly resolve this issue.

### How to debug a specific user individually?#prod-debug

The simplest solution is to have users use the [PageSpy browser extension](https://github.com/HuolalaTech/page-spy-extension), which is suitable for highly cooperative PC clients. However, this prerequisite is quite demanding.

So what if an H5 project in production wants to use PageSpy? Enabling it for all users is clearly impractical.

PageSpy's activation process involves only two steps:

1. Inject SDK via `<script>` tag in the `head`.
2. Instantiate.

PageSpy does not affect the project until the second step of instantiation. There are two approaches to debug a specific user:

- Dynamic HTML Response: If we can obtain a unique identifier for the user when requesting HTML, we can dynamically decide whether to inject `<script>` and instantiate logic before returning HTML to the user.

  <a href={dynamicInjectImg} target="_blank">
    <img src={dynamicInjectImg} />
  </a>

- User Gesture Activation: This typically requires the user's active cooperation. By default, inject the SDK but do not instantiate it, allowing the user to trigger debugging after a special gesture, see [config.gesture](./pagespy#config-gesture).

Note: Besides technical implementation, attention to legal compliance and security risks is essential.

### Page Panel Principle #page-principle

The Page panel renders document.documentElement.outerHTML of the client to an iframe on the debugging end, allowing direct element inspection via local console.

### Can the Page panel rendered on the client side interact directly? #page-interactive

It cannot interact directly. If interaction is needed, try entering code at the bottom of the Console panel and then checking the Page panel for interface feedback.

### Page panel style is incorrect?#page-style

Differences in rendering environments between the client and debugging end (e.g., client's browser version is Chrome 75, debugging end's is Chrome 120) or network restrictions when the debugging end accesses client-referenced resources may cause style discrepancies.

Therefore, styles are for reference only.

### Can the Page panel completely restore client content 100%?#page-reset

The SDK can "screenshot" the page and send it to the debugging end. However, due to:

- Larger data volume for images compared to text, increasing network transmission overhead
- Adding to SDK size and complexity
- For style errors, remote collaboration allows testers to provide precise feedback to developers

Therefore, Page panel styles are for reference only.

### Why does a globally installed package using pnpm cause errors when starting with pm2? #pnpm

Globally installed packages with pnpm are wrapped by a shell script used by pnpm. Thus, when executing pm2 start page-spy-api, it finds a shell script, and pm2 cannot interpret and execute it, resulting in an error.

<a href={pnpmInstallImg} target="_blank">
  <img src={pnpmInstallImg} />
</a>

Using yarn or npm installation resolves this issue. See related discussion: [Unitech/pm2#5416](https://github.com/Unitech/pm2/issues/5416).

### How to upgrade to the latest version after a new release?#upgrade

- If using Docker deployment:

```bash
# Update image
docker pull ghcr.io/huolalatech/page-spy-web:latest

# Stop running PageSpy container
docker stop pageSpy && docker rm -f pageSpy

# Run again
docker run -d --restart=always -p 6752:6752 --name="pageSpy" ghcr.io/huolalatech/page-spy-web:latest
```

- If using NPM Package deployment:

```bash
# Update package (yarn)
yarn global upgrade @huolala-tech/page-spy-api@latest

# Update package (npm)
npm install -g @huolala-tech/page-spy-api@latest

# Restart with pm2
pm2 restart page-spy-api
```

### Under what conditions will a room connection be automatically destroyed?#auto-destroy

> View configuration: https://github.com/HuolalaTech/page-spy-api/blob/master/room/local_room.go#L297-L323

- If a room is created and no SDK or debugging end enters, it will be destroyed after 1 minute (this scenario does not exist in actual use)
- If both SDK and debugging end disconnect, it will be destroyed after 1 minute
- If there is no data message interaction, it will be destroyed after 5 minutes
- If the connection duration exceeds 1 hour, it will be automatically destroyed

### Why can't Alipay Mini Program get global objects like `my.getCurrentPages()` when executing remote code? #alipay-global

Due to historical reasons, Alipay Mini Program restricts access to global objects. This can be set via the mini program configuration file or Alipay Mini Program IDE:

- IDE: Details -> Compilation Configuration -> Global Object (global/globalThis) Access Policy: Accessible (recommended)
- Configuration file: [https://opendocs.alipay.com/mini/03dbc3?pathHash=e876dc50#globalObjectMode](https://opendocs.alipay.com/mini/03dbc3?pathHash=e876dc50#globalObjectMode)

### Why are uploaded file logs missing?#offline-log

If you do not configure the `storageConfig` parameter in `config.json`, the uploaded file logs will be managed locally:

- Uploaded file logs are saved for up to the latest 10GB and 30 days by default, customizable via configuration
- Uploaded logs are saved in the `log` directory under the runtime directory. In Docker runtime, if the container is destroyed, logs will also be lost. Use directory mapping `-v ./log:/app/log -v ./data:/app/data` for persistence
